package com.ryuhi.jtools.idcard;

import com.ryuhi.jtools.idcard.util.StringUtil;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;

import java.util.Arrays;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.IntStream;

/**
 * 中国身份证号码验证 含大陆、香港、澳门
 * @author ryuhi<tobutatu@gmail.com>
 */
public enum IdCardEnum {

    /**
     * 中国身份证号码验证 大陆版
     */
    PRC {
        private static final int LegalLength = 18;
        private static final String specialChar = "X";
        @Override
        public boolean businessCheck(String idNumber) {
            String[] arr = idNumber.split("");
            String lastStr = arr[17];
            int[] intArr = Arrays.stream(Arrays.copyOf(arr, 17)).mapToInt(Integer::parseInt).toArray();
            int sum = 0;
            for (int i = 0; i< intArr.length; i++) {
                if (i ==0 || i == 10) {
                    sum = sum + 7 *  intArr[i];
                } else if (i == 1 || i == 11) {
                    sum = sum + 9 *  intArr[i];
                } else if (i == 2 || i == 12) {
                    sum = sum + 10 *  intArr[i];
                } else if (i == 3 || i == 13) {
                    sum = sum + 5 *  intArr[i];
                } else if (i == 4 || i == 14) {
                    sum = sum + 8 *  intArr[i];
                } else if (i == 5 || i == 15) {
                    sum = sum + 4 *  intArr[i];
                } else if (i == 6 || i == 16) {
                    sum = sum + 2 *  intArr[i];
                } else if (i == 7) {
                    sum = sum + intArr[i];
                } else if (i == 8) {
                    sum = sum + 6 *  intArr[i];
                } else if (i == 9) {
                    sum = sum + 3 *  intArr[i];
                }
            }
            int res = sum % 11;
            int rs;
            switch (res) {
                case 0:
                    rs = 1;
                    break;
                case 1:
                    rs = 0;
                    break;
                default:
                    rs = 12 - res;
                    break;
            }
            int compFlag = specialChar.equals(lastStr) ? 10 : Integer.parseInt(lastStr);
            return rs == compFlag;
        }

        @Override
        public boolean simpleCheck(String idNumber) throws IdCardException {
            if (StringUtils.isEmpty(idNumber)) {
                throw new IdCardException("身份证号码为空！");
            }
            idNumber = FullHalfEnum.FULL.convertString(idNumber).trim().toUpperCase();
            Pattern prcPattern = Pattern.compile("^[1-9]\\d{16}[X0-9]$");
            Matcher prc = prcPattern.matcher(idNumber);
            return (idNumber.length() == LegalLength && prc.find());
        }
    },

    /**
     * 中国身份证号码验证 香港版
     */
    HK {
        private final int[] LegalLength = {8, 9};
        @Override
        public boolean businessCheck(String idNumber) {
            boolean flag = true;
            String[] range = {"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y",
                    "Z"};
            int sum;
            String[] search = {"(",")"};
            idNumber = StringUtil.strReplace(search, "", idNumber);
            String newIdNumber = new StringBuffer(idNumber).reverse().toString();
            String[] arr = newIdNumber.split("");
            int count = arr.length;
            sum = IntStream.range(0, count).boxed().map(x -> {
                int key;
                if (x != 0 || !"A".equals(arr[0])) {
                    key = Integer.parseInt(arr[x]);
                } else {
                    key = 10;
                }
                return key * (x +1);
            }).mapToInt(Integer::valueOf).sum();
            int res = count -7;
            switch (res) {
                case 1:
                    int index = Arrays.asList(range).indexOf(arr[count-1]) + 1;
                    sum = sum + index * 8;
                    break;

                case 2:
                    int index1 = Arrays.asList(range).indexOf(arr[count-1]) + 1;
                    int index2 = Arrays.asList(range).indexOf(arr[count-2]) + 1;
                    sum = sum + index1 * 9 + index2 * 8;
                    break;

                default:
                    break;
            }
            int result = sum % 11;
            if (result != 0) {
                flag = false;
            }
            return flag;
        }

        @Override
        public boolean simpleCheck(String idNumber) throws IdCardException {
            if (StringUtils.isEmpty(idNumber)) {
                throw new IdCardException("身份证号码为空！");
            }
            idNumber = FullHalfEnum.FULL.convertString(idNumber).trim().toUpperCase();
            Pattern hkpattern = Pattern.compile("^[A-Z]{1,2}\\d{6}\\([A0-9]\\)$");
            Matcher hk = hkpattern.matcher(idNumber);
            int length = idNumber.length();
            return ArrayUtils.contains(LegalLength, length) && hk.find();
        }
    },

    /**
     * 中国身份证号码验证 澳门版
     */
    MO {
        private static final int LegalLength = 8;
        @Override
        public boolean businessCheck(String idNumber) {
            return true;
        }

        @Override
        public boolean simpleCheck(String idNumber) throws IdCardException {
            if (StringUtils.isEmpty(idNumber)) {
                throw new IdCardException("身份证号码为空！");
            }
            idNumber = FullHalfEnum.FULL.convertString(idNumber).trim().toUpperCase();
            Pattern mopattern = Pattern.compile("^[157]\\d{6}\\([\\d]\\)$");
            Matcher mo = mopattern.matcher(idNumber);
            return (idNumber.length() == LegalLength && mo.find());
        }
    };

    abstract boolean businessCheck(String idNumber);

    abstract boolean simpleCheck(String idNumber) throws IdCardException;

    public boolean check(String idNumber) throws IdCardException {
        return simpleCheck(idNumber) && businessCheck(idNumber);
    }

    public static void main(String[] args) throws IdCardException {
        String str = "33010619761030001X";
        boolean check = IdCardEnum.PRC.check(str);
        System.err.println(check);
    }
}
